
#include <QtNetwork>

#include "client.h"
#include "connection.h"
#include "peermanager.h"

//-------------------------------------

static const qint32 BroadcastInterval = 2000;
static const unsigned broadcastPort = 80;//45000;

//-------------------------------------

PeerManager::PeerManager( Client * client )
    : QObject( client )
{
    this->client = client;

    QStringList envVariables;
    envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*" << "HOSTNAME.*" << "DOMAINNAME.*";

    QStringList environment = QProcess::systemEnvironment();
    foreach( QString string, envVariables )
    {
        int index = environment.indexOf( QRegExp( string ) );
        if( index != -1 )
        {
            QStringList stringList = environment.at( index ).split( '=' );
            if( stringList.size() == 2 )
            {
                peername = stringList.at(1).toUtf8();
                break;
            }
        }
    }

    if( peername.isEmpty() )
        peername = "unknown";

    updateAddresses();
    serverPort = 0;

    broadcastSocket.bind( QHostAddress::Any, broadcastPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint );
    connect( &broadcastSocket, SIGNAL( readyRead() ), this, SLOT( readBroadcastDatagram() ) );

    broadcastTimer.setInterval( BroadcastInterval );
    connect( &broadcastTimer, SIGNAL( timeout() ), this, SLOT( sendBroadcastDatagram() ) );
}

//-------------------------------------

void PeerManager::setServerPort( int port )
{
    serverPort = port;
}

//-------------------------------------

QByteArray PeerManager::userName() const
{
    return peername;
}

//-------------------------------------

void PeerManager::startBroadcasting()
{
    broadcastTimer.start();
}

bool PeerManager::isLocalHostAddress(const QHostAddress &address)
{
    foreach( QHostAddress localAddress, ipAddresses )
    {
        if( address == localAddress )
            return true;
    }
    return false;
}

//-------------------------------------

void PeerManager::sendBroadcastDatagram()
{
    QByteArray datagram( peername );
    datagram.append( '@' );
    datagram.append( QByteArray::number( serverPort ) );

    bool validBroadcastAddresses = true;
    foreach( QHostAddress address, broadcastAddresses )
    {
        if( broadcastSocket.writeDatagram( datagram, address, broadcastPort ) == -1 )
            validBroadcastAddresses = false;
    }

    if( !validBroadcastAddresses )
        updateAddresses();
}

//-------------------------------------

void PeerManager::readBroadcastDatagram()
{
    while( broadcastSocket.hasPendingDatagrams() )
    {
        QHostAddress senderIp;
        quint16 senderPort;
        QByteArray datagram;
        datagram.resize( broadcastSocket.pendingDatagramSize() );
        if( broadcastSocket.readDatagram( datagram.data(), datagram.size(), &senderIp, &senderPort ) == -1 )
            continue;

        QList< QByteArray > list = datagram.split( '@' );
        if( list.size() != 2 )
            continue;

        int senderServerPort = list.at( 1 ).toInt();
        if( isLocalHostAddress( senderIp ) && senderServerPort == serverPort )
            continue;

        if( !client->hasConnection( senderIp ) )
        {
            Connection * connection = new Connection( this );
            emit newPMConnection( connection );
            connection->connectToHost( senderIp, senderServerPort );
        }
    }
}

//-------------------------------------

void PeerManager::updateAddresses()
{
    broadcastAddresses.clear();
    ipAddresses.clear();
    foreach( QNetworkInterface interface, QNetworkInterface::allInterfaces() )
    {
        foreach( QNetworkAddressEntry entry, interface.addressEntries() )
        {
            QHostAddress broadcastAddress = entry.broadcast();
            if( broadcastAddress != QHostAddress::Null && entry.ip() != QHostAddress::LocalHost )
            {
                broadcastAddresses << broadcastAddress;
                ipAddresses << entry.ip();
            }
        }
    }
}
